package com.tzy.spi.core;

import com.sun.org.apache.xpath.internal.objects.XBoolean;
import com.tzy.parser.core.Parser;
import com.tzy.spi.annotation.Adaptive;
import com.tzy.spi.exception.AdaptiveException;
import com.tzy.spi.exception.LoadClassException;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;

public class InterfaceInfo<T> implements InvocationHandler {

    private Class<T> interfaceClass;

    private String interfaceName;

    private String defaultName;

    private final ConcurrentHashMap<String,AdaptiveMethodInfo> adaptiveMethod=new ConcurrentHashMap<>();



    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String name = method.getName();
        if(!adaptiveMethod.containsKey(name)){
            throw new AdaptiveException(name + "没有被 @Adaptive 注解标识");
        }

        AdaptiveMethodInfo adaptiveMethodInfo = adaptiveMethod.get(method.getName());

        Object arg = args[adaptiveMethodInfo.metaIndex];

        String template = adaptiveMethodInfo.adaptive.value();

        String wrapper = adaptiveMethodInfo.adaptive.wrapper();


        //解析模板
        Parser parser = SpiLoader.loadInterface(Parser.class).getBean("default");

        if (!template.startsWith("metaObj")) {
            throw new AdaptiveException(template+" 不和 metaObj 匹配");
        }

        if (!wrapper.startsWith("metaObj") && !wrapper.equals("")) {
            throw new AdaptiveException(wrapper+" 不和 metaObj 匹配");
        }

        Parser.MetaObject metaObj = new Parser.MetaObject("metaObj", arg);

        //获得bean
        Object parse = parser.parse(metaObj, template);
        Object isWrapper=null;
        if(!wrapper.equals("")){
            isWrapper = parser.parse(metaObj, wrapper);
        }


        String adaptiveFormat = adaptiveFormat(parse);

        Boolean wrapperFormat = wrapperFormat(wrapper, isWrapper);


        T bean;
        try {
            bean = (T)SpiLoader.loadInterface(interfaceClass).getBean(adaptiveFormat,wrapperFormat);
        }catch (LoadClassException e){
            throw new AdaptiveException(template+" 解析结果 不能被映射为 string");
        }
        //返回
        return method.invoke(bean,args);
    }

    private String adaptiveFormat(Object parse) {
        if(parse==null){
            if(defaultName!=null){
                return defaultName;
            }else {
                throw new LoadClassException("没有指定的bean可以加载");
            }
        }
        return parse.toString();
    }

    private Boolean wrapperFormat(String wrapper, Object isWrapper) {

        if(isWrapper==null){
            return false;
        }


        if(isWrapper instanceof Boolean){
            return (Boolean) isWrapper;
        }else{//基础
            try {
                return Boolean.valueOf(String.valueOf(isWrapper));
            }catch (Exception e){
                throw new AdaptiveException(wrapper+" 解析结果 不能被映射为 boolean");
            }
        }
    }

    public void setInterfaceClass(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public void setDefaultName(String defaultName) {
        this.defaultName = defaultName;
    }

    public Class<T> getInterfaceClass() {
        return interfaceClass;
    }

    public String getInterfaceName() {
        return interfaceName;
    }

    public String getDefaultName() {
        return defaultName;
    }


    public void addAdaptiveMethodInfo(String methodName, Adaptive adaptive , int metaIndex){
        this.adaptiveMethod.putIfAbsent(methodName,new AdaptiveMethodInfo(methodName,adaptive,metaIndex));
    }

    public int adaptiveNum() {
        return this.adaptiveMethod.size();
    }


    private static class AdaptiveMethodInfo{

        public AdaptiveMethodInfo(String methodName,Adaptive adaptive , int metaIndex) {
            this.methodName = methodName;
            this.adaptive = adaptive;
            this.metaIndex = metaIndex;

        }


        private String methodName;

        private Adaptive adaptive ;

        private int metaIndex;

    }
}
